#include "ModBusToPrivateBus.h"
#include "ModBusToPrivateBus_CRC.h"
#include "ModBusToPrivateBus_port.h"
#include "PrivateBusMaster.h"
uint8_t ModBusSlaveID;
uint8_t *ModBus_Uart_Tx_Ptr = NULL;//串口发送指针，指向上DMA发送的缓冲区

//后续可以通过这个运算偏移地址以及，要申请的虚拟寄存器空间
uint8_t Addr_Offset[16][4]={0};//地址偏移，记录不同块的结束地址，长度为该结束地址减去上一块的结束地址
//uint16_t ModBus_CPT[8]={0};//串行链路诊断计数器

//定时发送功能
uint16_t ModBus_CallBack_delay=0;
uint16_t ModBus_CallBack_delay_cnt=0;
uint8_t *ModBus_callback_tx_buffer=NULL;
uint16_t ModBus_callback_tx_len=0;

//标志是否繁忙，是否在处理上一个ModBus协议
uint8_t ModBus_busy_flag=0;



/**
  * @bried  初始化ModBus从机
  * @note   设置从机地址，发送函数缓冲区，接受消息根节点（消息回调）
  * @param  None
  * @retval None
  */
void ModeBus_Init(uint8_t slave_id){
    ModBusSlaveID=slave_id;
}
/**
  * @bried  解析任务
  * @note
  * @param
  * @retval None
  */
void ModeBus_DealWith(uint8_t* _data,uint16_t _len){
    //判断是否是自己的消息
    if(_data[0]!=ModBusSlaveID&&_data[0]!=0x00)return;//从机地址检测 0为广播地址
    //进行CRC校验，看是否有误
    uint8_t CRC_bit_H,CRC_bit_L;
    PB_Get_CRC(_data,_len-2,&CRC_bit_L,&CRC_bit_H);
    //接收CRC检验有误
    if ((CRC_bit_L!=_data[_len-2])||(CRC_bit_H!=_data[_len-1]))return;
    ++ModBus_Slave_Message_Count;//记录处理该节点接收报文数

    if (ModBus_busy_flag){
        //返回消息表明设备繁忙
    }
    ModBus_busy_flag=1;//设置状态为繁忙

    //设置定时发送缓冲区
    ModBus_CallBack_delay_cnt=ModBus_CallBack_delay;
    ModBus_callback_tx_buffer=ModBus_Uart_Tx_Ptr;
    ModBus_callback_tx_len=0;

    //简单解析，转换为对应拓展模块的地址
    ModBus_To_PrivateBus(_data,_len);
    if (_data[0]==0x00){//对主机的信息
        ModBus_callback_tx_buffer[0]=ModBusSlaveID;//从机地址
        ModBus_callback_tx_buffer[1]=_data[1];//功能码
        uint16_t reg_count=__2U8TU16(_data[4],_data[5]);
        switch (ModBus_callback_tx_buffer[1]){
            case 0x03:
                reg_count=__2U8TU16(_data[4],_data[5]);
                ModBus_callback_tx_buffer[2]=(reg_count)/8;
                if (reg_count%8!=0)ModBus_callback_tx_buffer[2]++;
                //设置消息长度
                ModBus_callback_tx_len=ModBus_callback_tx_buffer[2]+5;
                break;
            case 0x06:
                ModBus_callback_tx_len=9;//不需要CRC校验和消息编号添加
                memcpy(ModBus_callback_tx_buffer,_data,ModBus_callback_tx_len);
                break;
            case 0x08:
                ModBus_callback_tx_len=_len;
                memcpy(ModBus_callback_tx_buffer,_data,_len);//直接回显消息的配置
                break;
            case 0x10://连续写，只需要重新生成CRC消息
                ModBus_callback_tx_len=8;
                memcpy(ModBus_callback_tx_buffer,_data,ModBus_callback_tx_len-2);
                break;
            default:
                break;
        }
        switch (ModBus_callback_tx_buffer[1]) {
            case 0x03:PB_Port_03(__2U8TU16(_data[2],_data[3]),reg_count,&(ModBus_callback_tx_buffer[3]));break;
            case 0x06:PB_Port_06(__2U8TU16(_data[2],_data[3]),(__2U8TU16(_data[4],_data[5])));return;
            case 0x08:PB_Port_08(__2U8TU16(_data[2],_data[3]),__2U8TU16(_data[4],_data[5]),&(ModBus_callback_tx_buffer[4]));
            case 0x10:PB_Port_10(__2U8TU16(_data[2],_data[3]),reg_count,&(_data[7]));break;
            default:
                break;
        }
        //CRC校验
        PB_Get_CRC(PrivateBus_Uart_Tx_Buffer,ModBus_callback_tx_len-2,&CRC_bit_L,&CRC_bit_H);
        ModBus_callback_tx_buffer[ModBus_callback_tx_len-2]=CRC_bit_L;
        ModBus_callback_tx_buffer[ModBus_callback_tx_len-1]=CRC_bit_H;
        return;
    } else{
        //访问拓展模块，向拓展模块发送消息
        PrivateBus_Master_Send_Async(_data,_len,ModBus_callback_tx_buffer,&ModBus_callback_tx_len);
    }
}

//这种方式不能 跨拓展设备访问连续寄存器，否则后续寄存器的值不确定
/**
  * @bried  将ModBus收到的消息解析为PrivateBus的消息指令
  * @note   该函数不对消息进行验证，只对消息进行转换，不申请空间，_len不包含消息号的一个长度
  * @param  None
  * @retval None
  */
void ModBus_To_PrivateBus(uint8_t* _data,uint16_t _len){
    uint16_t reg=__2U8TU16(_data[2],_data[3]);
    uint8_t block=0;
    switch (_data[1]) {//判断是哪个块的消息
        case 0x01:
        case 0x05:
        case 0x0F:
            block=0;
            break;
        case 0x02:
            block=1;
            break;
        case 0x04:
            block=2;
            break;
        case 0x03:
        case 0x06:
        case 0x10:
            block=3;
            break;
        default:
            break;
    }
    uint8_t index=0;
    while (true){
        if (index<=PrivateBus_Slave_Num){//选择从机在正常值范围内
            if(reg<Addr_Offset[index][block]){//找到正确的从机位置
                break;
            }else{
                ++index;
            }
        } else{
            index=0;
            break;
        }
    }
    _data[0]=index;//选择这个从机
    if (_data[0]!=0){//添加寄存器地址偏移
        reg+=Addr_Offset[_data[0]-1][block];
    }
    //重新进行CRC校验
    PB_Get_CRC(_data,_len-2,&(_data[_len-2]),&(_data[_len-1]));
}
/**
  * @bried  PrivateBus接收的到数据解析为主机的ModBus协议消息
  * @note   该函数不对消息进行验证，只对消息进行转换，_len含有私有协议的消息编号长度
  * @param  None
  * @retval None
  */
void PrivateBus_To_ModBus(uint8_t* _data,uint16_t _len){
    uint8_t PBSlaveID=_data[0];
    uint16_t reg;
    _data[0]=ModBusSlaveID;//转换从机地址
    switch (_data[1]) {//计算地址偏移
        case 0x05:
        case 0x0F:
            reg=__2U8TU16(_data[2],_data[3])+Addr_Offset[PBSlaveID-1][0];
            _data[2]=reg>>8;
            _data[3]=reg&0x00FF;
            break;
        case 0x06:
        case 0x10:
            reg=__2U8TU16(_data[2],_data[3])+Addr_Offset[PBSlaveID-1][3];
            _data[2]=reg>>8;
            _data[3]=reg&0x00FF;
            break;
        default:
            break;
    }
    PB_Get_CRC(_data,_len,&(_data[_len-2]),&(_data[_len-1]));
}
/**
  * @bried  发型消息任务循环，判断任务是否需要发送，并发送
  * @note   1ms任务
  * @param  None
  * @retval None
  */
void ModeBus_Send_Task(void){
    if (ModBus_callback_tx_buffer){
        if(ModBus_CallBack_delay_cnt){
            ModBus_CallBack_delay_cnt--;
        } else{//发送消息
            ModBus_Uart_Send(ModBus_callback_tx_buffer,ModBus_callback_tx_len);
            ModBus_callback_tx_buffer=NULL;
        }
    }

}


